package org.dreamwork.tools.code.analyzer.jfx;

import com.google.gson.Gson;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.web.WebView;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
import org.dreamwork.injection.IObjectContext;
import org.dreamwork.tools.code.analyzer.core.data.Entity;
import org.dreamwork.tools.code.analyzer.core.model.ChoosePathPayload;
import org.dreamwork.tools.code.analyzer.core.model.EntityType;
import org.dreamwork.tools.code.analyzer.core.service.IEntityService;
import org.dreamwork.tools.code.analyzer.jfx.js.JSBridge;
import org.dreamwork.tools.code.analyzer.jfx.model.Module;
import org.dreamwork.tools.code.analyzer.jfx.model.Project;
import org.dreamwork.util.DateUtil;
import org.dreamwork.util.StringUtil;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

public class MainController implements Initializable {
    @FXML WebView web;

    private IObjectContext context;

    private String index;

    private JSBridge bridge;
    private Stage owner;
    private File lastLocation;
    private Entity project;
    private List<Entity> modules;

    private final DirectoryChooser chooser = new DirectoryChooser ();

    public MainController () {
        Class<?> type = getClass ();
        ClassLoader loader = type.getClassLoader ();
        String resourceName = type.getCanonicalName ().replace ('.', '/') + ".class";
        URL url = loader.getResource (resourceName);
        boolean runInJar = false;
        if (url != null) {
            String form = url.toExternalForm ();
            runInJar = form.contains (".jar!");
        }

        if (runInJar) {
            url = loader.getResource ("html/index.html");
            if (url != null) {
                index = url.toExternalForm ();
            }
        } else {
            File file = new File ("../../src/main/resources/html/index.html");
            try {
                index = file.toURI ().toURL ().toExternalForm ();
            } catch (IOException ignore) {}
        }
    }

    public void setContext (IObjectContext context) {
        this.context = context;
    }

    public void setOwner (Stage owner) {
        this.owner = owner;
        if (bridge != null) {
            bridge.setOwner (owner);
        }
    }

    @Override
    public void initialize (URL location, ResourceBundle resources) {
        if (index != null) {
            bridge = new JSBridge (web.getEngine (), index, this::onPageLoaded);
//            web.setMinWidth (1000);
            bridge.setJavaScriptActionExecutor (this::performAction);
            if (owner != null) {
                bridge.setOwner (owner);
            }
        }
    }

    private void onPageLoaded (JSObject window) {
    }

    private Object performAction (String action, Object params) {
        Gson g = new Gson ();
        if (!StringUtil.isEmpty (action)) switch (action.trim ()) {
            case "projects":
                return getProjectList ();

            case "load-project":
                String projectId = (String) params;
                return getProject (projectId);

            case "save-project":
                Project project = g.fromJson ((String) params, Project.class);
                return saveProject (project);

            case "save-module":
                Module module = g.fromJson ((String) params, Module.class);
                return saveModule (module);

            case "close-project":
                this.project = null;
                if (this.modules != null) {
                    this.modules.clear ();
                }
                break;

            case "choose-dir":
                ChoosePathPayload payload = null;
                if (params != null) {
                    payload = g.fromJson ((String) params, ChoosePathPayload.class);
                }
                return chooseDirectory (payload);
        }
        return null;
    }

    private String chooseDirectory (ChoosePathPayload payload) {
        if (payload != null && !StringUtil.isEmpty (payload.path))
            chooser.setInitialDirectory (new File (payload.path));
        else if (lastLocation != null)
            chooser.setInitialDirectory (lastLocation);
        else if (this.project != null)
            chooser.setInitialDirectory (new File (this.project.getPath ()));

        File file = chooser.showDialog (owner);
        if (file != null) {
            lastLocation = file;
            try {
                String path = file.getCanonicalPath ();
                if (payload != null && payload.type == EntityType.module) {
                    Path base = Paths.get (this.project.getPath ());
                    path = base.relativize (file.toPath ()).toString ();
                }
                return path;
            } catch (IOException e) {
                e.printStackTrace ();
            }
        }
        return null;
    }

    private List<Project> getProjectList () {
        IEntityService service = context.getBean (IEntityService.class);
        return service.loadAllProjects ().stream()
                .map (e -> {
                    Project project = new Project ();
                    project.id   = e.getId ();
                    project.name = e.getName ();
                    project.path = e.getPath ();
                    project.pattern = e.getIgnored ();
                    return project;
                })
                .collect (Collectors.toList ());
    }

    private Project getProject (String projectId) {
        IEntityService service = context.getBean (IEntityService.class);
        Entity entity = service.getByPrimary (Entity.class, projectId);
        this.project = entity;
        this.modules = service.loadAllModules (projectId);

        Project project = new Project ();
        project.id   = entity.getId ();
        project.name = entity.getName ();
        project.path = entity.getPath ();
        project.pattern = entity.getIgnored ();
        project.modules = modules.stream()
                .map (e -> {
                    Module module = new Module ();
                    module.name = e.getName ();
                    module.path = e.getPath ();
                    module.pattern = e.getIgnored ();
                    return module;
                }).collect (Collectors.toMap (m -> m.name, m -> m));
        return project;
    }

/*
    private String createProject (Project project) {
        String now = DateUtil.instance ().formatDateTime ();
        Entity p = new Entity ();
        p.setIgnored (project.pattern);
        p.setPath (project.path);
        p.setName (project.name);
        p.setType (EntityType.project);
        p.setId (StringUtil.uuid ());
        p.setCreated (now);

        List<Entity> modules = null;
        if (project.modules != null && !project.modules.isEmpty ()) {
            modules = project.modules.values ().stream().map (module -> {
                Entity e = new Entity ();
                e.setType (EntityType.module);
                e.setId (StringUtil.uuid ());
                e.setBelongsTo (p.getId ());
                e.setName (module.name);
                e.setPath (module.path);
                e.setCreated (now);
                if (!StringUtil.isEmpty (module.pattern)) {
                    e.setIgnored (module.pattern);
                } else {
                    e.setIgnored (project.pattern);
                }
                return e;
            }).collect (Collectors.toList ());
        }
        IEntityService service = context.getBean (IEntityService.class);
        service.save (p, modules);

        return p.getId ();
    }
*/

    private String saveProject (Project project) {
        IEntityService service = context.getBean (IEntityService.class);
        Entity p = new Entity ();
        p.setIgnored (project.pattern);
        p.setPath (project.path);
        p.setName (project.name);
        p.setType (EntityType.project);
        if (StringUtil.isEmpty (project.id))
            p.setId (StringUtil.uuid ());
        else
            p.setId (project.id);
        p.setCreated (DateUtil.instance ().formatDateTime ());

        if (StringUtil.isEmpty (project.id)) {
            service.save (p);
        } else {
            service.update (p);
        }
        this.project = p;
        return p.getId ();
    }

    private String saveModule (Module module) {
        Entity e = new Entity ();
        e.setType (EntityType.module);
        e.setId (StringUtil.uuid ());
        e.setBelongsTo (project.getId ());
        e.setName (module.name);
        e.setPath (module.path);
        e.setCreated (DateUtil.instance ().formatDateTime ());

        String pattern = project.getIgnored ();
        if (!StringUtil.isEmpty (module.pattern)) {
            pattern += ';' + module.pattern;
        }
        e.setIgnored (pattern);

        IEntityService service = context.getBean (IEntityService.class);
        service.save (e);
        if (this.modules == null) {
            this.modules = new ArrayList<> ();
        }
        this.modules.add (e);
        return e.getId ();
    }
}